﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Brain.Rendering
{
    public class PostProcessor_CPUGrid : IBasePostProcessor, IDisposable
    {
        public IGridAnimation GridAnimation { get; protected set; }

        /// <summary>
        /// Effect used for rendering the Grid
        /// </summary>
        public Effect Effect;

        internal bool UsingBasicEffect = false;

        /// <summary>
        /// Vertices of the Grid
        /// </summary>
        public VertexPositionTexture[] Vertices { get; protected set; }

        /// <summary>
        /// Indices of the Grid
        /// </summary>
        public int[] Indices { get; protected set; }

        /// <summary>
        /// How much collums this Grid has(X)
        /// </summary>
        public int Colums { get; protected set; }

        /// <summary>
        /// How much rows this Grid has(Y)
        /// </summary>
        public int Rows { get; protected set; }

        /// <summary>
        /// Size (in pixels) of a cell
        /// </summary>
        public Vector2 CellSize { get; protected set; }

        public Texture2D InputTexture { get; set; }

        public bool RequestOutputTexture { get; set; }

        public RenderTarget2D OutputTexture { get; set; }

#if SILVERLIGHT
        public VertexBuffer VertexBuffer;
        public IndexBuffer IndexBuffer;
#endif

        /// <summary>
        /// This Constructor will use as Effect a BasicEffect
        /// </summary>
        /// <param name="colums"></param>
        /// <param name="rows"></param>
        /// <param name="cellSize"></param>
        /// <param name="gs"></param>
        public PostProcessor_CPUGrid(int colums, int rows, Vector2 cellSize, int resWidth, int resHeight)
        {
            ReMakeGeometry(colums, rows, cellSize);

            Effect = new BasicEffect(Engine.Instance.GraphicsDevice)
            {
                TextureEnabled = true,
                View = Matrix.Identity,
                World = Matrix.Identity,
                Projection = Matrix.CreateOrthographicOffCenter(0, resWidth, resHeight, 0, 0, 10)
            };
            UsingBasicEffect = true;
        }
        public PostProcessor_CPUGrid(int colums, int rows, Vector2 cellSize, Effect fx)
        {
            ReMakeGeometry(colums, rows, cellSize);
            this.Effect = fx;
        }

        public virtual void SetAnimation(IGridAnimation anim)
        {
            this.GridAnimation = anim;
            if (anim != null)
            {
                this.GridAnimation.Grid = this;
            }
        }

        public virtual void Dispose()
        {
            this.Vertices = null;
            if (this.Effect != null)
            {
                this.Effect.Dispose();
                this.Effect = null;
            }
            Indices = null;
        }

        public virtual void ReMakeGeometry(int colums, int rows, Vector2 cellSize)
        {
            this.Colums = colums;
            this.Rows = rows;
            this.CellSize = cellSize;

            Vertices = new VertexPositionTexture[(colums + 1) * (rows + 1)];
            Indices = new int[colums * rows * 6];

            for (int x = 0; x < colums + 1; x++)
            {
                for (int y = 0; y < rows + 1; y++)
                {
                    int index = y * (colums + 1) + x;
                    VertexPositionTexture vertex = new VertexPositionTexture();
                    vertex.Position = new Vector3(new Vector2(x, y) * CellSize, 0);
                    vertex.TextureCoordinate = this.GetDefaultUV(index);
                    Vertices[index] = vertex;
                }
            }

            int indexPos = 0;
            for (int i = 0; i < colums; i++)
            {
                for (int j = 0; j < rows; j++)
                {
                    int v0 = j * (colums + 1) + i;
                    int v1 = j * (colums + 1) + i + 1;
                    int v2 = (j + 1) * (colums + 1) + i;
                    int v3 = (j + 1) * (colums + 1) + i + 1;

                    Indices[indexPos] = (short)v0;
                    Indices[indexPos + 1] = (short)v1;
                    Indices[indexPos + 2] = (short)v2;
                    Indices[indexPos + 3] = (short)v2;
                    Indices[indexPos + 4] = (short)v1;
                    Indices[indexPos + 5] = (short)v3;
                    indexPos += 6;
                }
            }

#if SILVERLIGHT
            VertexBuffer = new VertexBuffer(Engine.Instance.GraphicsDevice, typeof(VertexPositionColorTexture),
                Vertices.Length, BufferUsage.None);
            VertexBuffer.SetData<VertexPositionColorTexture>(Vertices);

            IndexBuffer = new IndexBuffer(Engine.Instance.GraphicsDevice, IndexElementSize.SixteenBits, Indices.Length,
                BufferUsage.None);
            IndexBuffer.SetData<short>(Indices);
#endif
        }

        public virtual void Update()
        {
            if (GridAnimation != null &&
                GridAnimation.ShouldUpdate)
            {
                GridAnimation.Update();
            }
        }

        /// <summary>
        /// Draw the grid. If you are not using Basic Effect,
        /// you need to set-up the Effect before calling this method
        /// </summary>
        /// <param name="graphicsDevice"></param>
        public virtual void Draw()
        {
            if (this.RequestOutputTexture)
            {
                Engine.Instance.GraphicsDevice.SetRenderTarget(this.OutputTexture);
            }
            if (UsingBasicEffect)
            {
                Engine.Instance.GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

                BasicEffect fx = Effect as BasicEffect;
                fx.Texture = this.InputTexture;
                foreach (EffectPass pass in fx.CurrentTechnique.Passes)
                {
                    pass.Apply();
                }
            }
#if !SILVERLIGHT
            Engine.Instance.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(
                PrimitiveType.TriangleList, Vertices, 0, Vertices.Length, Indices, 0, Indices.Length / 3);
#else
            Engine.Instance.GraphicsDevice.SetVertexBuffer(this.VertexBuffer);
            Engine.Instance.GraphicsDevice.Indices = this.IndexBuffer;
            Engine.Instance.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                0, 0, VertexBuffer.VertexCount, 0, VertexBuffer.VertexCount / 3);
#endif
            if (this.RequestOutputTexture)
            {
                Engine.Instance.GraphicsDevice.SetRenderTarget(null);
            }
        }

        /// <summary>
        /// Reset all UVs
        /// </summary>
        public void ResetUVs()
        {
            for (int i = 0; i < Vertices.Length; i++)
            {
                VertexPositionTexture v = Vertices[i];
                v.TextureCoordinate = GetDefaultUV(i);
                Vertices[i] = v;
            }
        }

        /// <summary>
        /// Get Texture Coordinate of the element at position <b>index</b>
        /// </summary>
        public Vector2 GetUV0(int index)
        {
            return Vertices[index].TextureCoordinate;
        }

        /// <summary>
        /// Set Texture Coordinate of the element at position <b>index</b>
        /// </summary>
        public void SetUV0(int index, Vector2 value)
        {
            Vertices[index].TextureCoordinate = value;
        }

        /// <summary>
        /// Get the default Texture Coordinate of the element at position <b>index</b>
        /// </summary>
        public Vector2 GetDefaultUV(int index)
        {
            int i = index % (Colums + 1);
            int j = index / (Colums + 1);
            return new Vector2((float)i / Colums, (float)j / Rows);
        }


    }
}
